#include "include/rpc_server.h"
#include "include/event_manager.h"

#include "rpc/net/socket.h"
#include "rpc/net/acceptor.h"
#include "rpc/net/connection.h"

namespace {
class NewConnClosure : public Closure {
  public:
    NewConnClosure(rpc::Connection* trans)
        : trans_(trans) {
      CHECK_NOTNULL(trans);
    }

    void Run() {
      CHECK(trans_->Init());
      delete this;
    }

  private:
    rpc::Connection* trans_;

    DISALLOW_COPY_AND_ASSIGN(NewConnClosure);
};
}

namespace rpc {

static void HandlerAcceptEvent(int32 fd, void* ctx, int32 event) {
  Acceptor* acceptor = static_cast<Acceptor*>(ctx);
  CHECK(acceptor != NULL);

  acceptor->HandleEvent();
}

Acceptor::Acceptor(RpcServer* owner, EventManager* poller)
    : owner_(owner), ev_mgr_(poller), protocol_(NULL) {
  accept_socket_.reset(new Socket);
  CHECK_NOTNULL(accept_socket_.get());
  accept_event_.reset(new Event);
  CHECK_NOTNULL(accept_event_.get());
}

Acceptor::~Acceptor() {
  ev_mgr_->Del(accept_event_.get(), EV_READ);
}

bool Acceptor::Init(const std::string& ip, uint16 port) {
  CHECK_NOTNULL(protocol_);
  Addr addr(ip, port);
  accept_socket_->SetBlock(false);
  if (!accept_socket_->Listen(addr)) {
    LOG(WARNING)<< "listen fail, address: " << addr.DebugString();
    return false;
  }
  LOG(INFO)<< "accept fd: " << accept_socket_->Fd();

  accept_event_->fd = accept_socket_->Fd();
  accept_event_->event = EV_READ;
  accept_event_->cb = HandlerAcceptEvent;
  accept_event_->arg = this;

  return ev_mgr_->Add(accept_event_.get(), EV_READ);
}

void Acceptor::HandleEvent() {
  Socket* s = accept_socket_->Accept(NULL);
  if (s == NULL) {
    return;
  }

  EventManager* ev_mgr = owner_->getPoller();
  Connection* trans(new Connection(ev_mgr, s));
  CHECK_NOTNULL(trans);
  trans->SetProtocol(protocol_);
  trans->SetAttr(protocol_->NewAttr());
  trans->SetCloseClosure(
      NewCallback(owner_, &RpcServer::RemoveTransaction, trans));
  ev_mgr->RunInLoopThread(new NewConnClosure(trans));

  VLOG(1) << "new trnas: " << s->DebugString();
  owner_->AddTransaction(trans);
}

}
